{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c8b4bd13",
   "metadata": {},
   "source": [
    "### langchain-tavily"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "df2796a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "! uv add -qU langchain-tavily -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e606b778",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv \n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5302fd00",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\李林\\AppData\\Local\\Temp\\ipykernel_45104\\1255991248.py:2: LangChainDeprecationWarning: The class `TavilySearchResults` was deprecated in LangChain 0.3.25 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-tavily package and should be used instead. To use it run `pip install -U :class:`~langchain-tavily` and import as `from :class:`~langchain_tavily import TavilySearch``.\n",
      "  search = TavilySearchResults(max_results=2)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[{'title': '苹果2025 年全球开发者大会（WWDC 2025）官宣，iOS19将迎来 ...',\n",
       "  'url': 'https://zhuanlan.zhihu.com/p/1908951504204836940',\n",
       "  'content': '苹果公司正式宣布，2025 年全球开发者大会（WWDC 2025）将于当地时间6 月9 日至13 日（北京时间6 月10 日至14 日）举行。 本届大会将继续以线上形式为主，',\n",
       "  'score': 0.8827621},\n",
       " {'title': 'Apple 開發者大會「WWDC 2025」即將登場！4 大亮點預告搶先看',\n",
       "  'url': 'https://www.gq.com.tw/article/apple-%E9%96%8B%E7%99%BC%E8%80%85%E5%A4%A7%E6%9C%83-wwdc2025',\n",
       "  'content': '[![Image 5: iphone 17](https://media.gq.com.tw/photos/683056c975b896245a71634f/1:1/w_775%2Cc_limit/iphone%252017.jpg)](https://www.gq.com.tw/article/apple-iphone17-%E6%90%B6%E5%85%88%E6%9B%9D%E5%85%89)\\n\\n* * *\\n\\n**「WWDC 2025」亮點預告**\\n-------------------\\n\\nWWDC 2025 將於台灣時間 6 月 10 日凌晨 1 點正式揭開序幕，通常開場的 Keynote 發表會將會是整個活動最關鍵的亮點之一。屆時將一次公開全系列 Apple 作業系統的更新內容，包括 iOS、iPadOS、macOS、watchOS、tvOS 等。\\n\\n**亮點 1：統一視覺設計** [...] Apple 一年一度的全球開發者大會（WWDC）將於台灣時間 2025 年 6 月 10 日至 14 日重磅登場！這場原先以開發者為主的活動，如今已成為全球矚目的 Apple 軟體與服務更新重點發表會，預計將為我們帶來為期一週的創新科技盛會。除了各版本作業系統（如 iOS、macOS 等）即將迎來例行更新外，預計 Apple Intelligence 也將會有新消息公布，同時還可能有些輕量級的硬體新產品提前亮相。在萬眾矚目的大會正式展開前，讓我們先一起來看看 Apple 今年預計會有哪些亮點發表吧！\\n\\n繼續閱讀 [...] **亮點 3：AI 功能更新**\\n\\n今年 AI 可能不像去年 Apple Intelligence 剛推出時，扮演如此關鍵的角色，但仍預計會推出更新功能，像是類似 ChatGPT 的原生 AI 助理雛形，以及注入強大 AI 功能的新一代 Siri。\\n\\n其他幾項令人期待的新功能則包括，透過 Siri 和 AirPods 進行即時語音翻譯，並透過全系列裝置的智慧電池管理功能，延長電池壽命、提升續航力。\\n\\n**亮點 4：硬體新品**\\n\\n至於硬體方面，目前仍尚未公布是否會有全新裝置亮相。不過仍有傳聞指出，Apple 可能會針對周邊產品進行更新，例如推出 AirTag 第二代等全新配件。\\n\\nAn illustration shows an Apple Intelligence logo displayed on a smartphone in Suqian, China, on March 10, 2025. (Photo Illustration by Costfoto/NurPhoto via Getty Images)NurPhoto/Getty Images\\n\\n* * *',\n",
       "  'score': 0.8810534}]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "search = TavilySearchResults(max_results=2)\n",
    "search.invoke(\"苹果2025WWDC发布会\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "855cfd1d",
   "metadata": {},
   "source": [
    "### 集成大模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b1fff151",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `tavily_search_results_json` with `{'query': '苹果2025WWDC发布会时间'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m[{'title': '苹果WWDC 2025定档6月10日，将举办visionOS及空间音频技术专场 ...', 'url': 'https://news.nweon.com/129874', 'content': '**Vision Pro QQ群交流**：[653565822](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=12r0nSXG0xIB1dkAYnRn4xdYYj7d4WT-&authKey=Xp%2FuDlwzoJ%2BHQ1bh2EcckpQSNHRE2uAb38%2FouHEt59wiH9ODyI2KLSkZgMmNw6ra&noverify=0&group_code=653565822)\\n\\n（**[映维网Nweon](https://www.nweon.com/) 2025年05月21日**）[苹果](https://news.nweon.com/tag/Apple \"【查看含有[苹果]标签的文章】\")正式宣布，WWDC 2025将于太平洋时间6月9日至13日举行，亦即北京时间6月10日至14日。开幕主题演讲定于北京时间6月10日凌晨1点。\\n\\n本届大会将继续以线上形式为主，并在Apple Park举办线下特别活动。值得一提的是，苹果将以线上形式举办一系列的小组实验室，包括visionOS和沉浸视频与空间音频技术。 [...] 举办时间是2025年6月14日凌晨 3:00 – 凌晨 4:00 (GMT+8)\\n\\n请在6月14日凌晨3:59 (GMT+8) 之前[注册](https://developer.apple.com/wwdc25/labs/M9QMRR4D56/dashboard)。\\n\\n映维网（nweon.com）\\n\\n映维网（nweon.com）\\n\\n映维网（nweon.com）\\n\\n映维网（nweon.com）\\n\\n**本文链接**：[https://news.nweon.com/129874](https://news.nweon.com/129874)\\n\\n**转载须知**：转载摘编需注明来源映维网并保留[本文链接](https://news.nweon.com/129874)\\n\\n**素材版权**：除额外说明，文章所用图片、视频均来自文章关联个人、企业实体等提供 [...] ![Image 3](https://news.nweon.com/wp-content/uploads/2025/05/02e4edb9680940042905ccf55af6d443.jpg)\\n\\n**visionOS 小组实验室**\\n\\n参加线上直播活动，与苹果工程师和设计师一起深入了解 WWDC25。在这个以空间计算为主题的活动中，你可以提出问题、获取建议，并实时关注围绕大会一周的相关重磅发布展开的精彩讨论。活动语言为英语。\\n\\n举办时间是2025年6月12日早上 6:00 – 早上 7:00 (GMT+8)\\n\\n请在6月12日早上 6:59 (GMT+8) 之前[注册](https://developer.apple.com/wwdc25/labs/X79C3GP3CA/dashboard)。\\n\\n**浸视频与空间音频技术小组实验室**\\n\\n参加线上直播活动，与苹果工程师和设计师一起深入了解WWDC25。在这个以沉浸视频与空间音频技术为主题的活动中，你可以提出问题、获取建议，并实时关注围绕大会一周的相关重磅发布展开的精彩讨论。活动语言为英语。', 'score': 0.9477354}, {'title': 'WWDC 2025即将开幕苹果公布活动详细日程安排 - 新浪财经', 'url': 'https://finance.sina.com.cn/tech/roll/2025-05-21/doc-inexhwwk8161224.shtml', 'content': '再过三周，苹果WWDC 2025将于6月9 - 13日线上举行。当地时间6月9日上午10点（北京时间10日凌晨1点）主题演讲开始，将发布iOS 19、macOS 16等系统新', 'score': 0.9328032}]\u001b[0m\u001b[32;1m\u001b[1;3m苹果2025年WWDC开发者大会的召开时间如下：\n",
      "\n",
      "- **活动时间**：2025年6月9日至13日（太平洋时间），即北京时间6月10日至14日。\n",
      "- **开幕主题演讲**：北京时间6月10日凌晨1点。\n",
      "\n",
      "大会将以线上形式为主，并在Apple Park举办线下特别活动。更多详细日程和活动安排可以通过[苹果官方页面](https://developer.apple.com/wwdc25/)查看。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '请问苹果2025WWDC发布会召开的时间是？',\n",
       " 'output': '苹果2025年WWDC开发者大会的召开时间如下：\\n\\n- **活动时间**：2025年6月9日至13日（太平洋时间），即北京时间6月10日至14日。\\n- **开幕主题演讲**：北京时间6月10日凌晨1点。\\n\\n大会将以线上形式为主，并在Apple Park举办线下特别活动。更多详细日程和活动安排可以通过[苹果官方页面](https://developer.apple.com/wwdc25/)查看。'}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import AgentExecutor, create_tool_calling_agent, tool\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain.chat_models import init_chat_model\n",
    "\n",
    "tools = [search]\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"你是一名助人为乐的助手，并且可以调用工具进行网络搜索，获取实时信息。\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "        (\"placeholder\", \"{agent_scratchpad}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "# 初始化模型\n",
    "model = init_chat_model(\"deepseek-chat\", model_provider=\"deepseek\")\n",
    "\n",
    "agent = create_tool_calling_agent(model, tools, prompt)\n",
    "\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "\n",
    "agent_executor.invoke({\"input\": \"请问苹果2025WWDC发布会召开的时间是？\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50736271",
   "metadata": {},
   "source": [
    "### 实战：多智能体协作实现浏览器自动化\n",
    "\n",
    "  正如上述我们使用的create_tool_calling_agent方法，它其实在langChain中是一个通用的用来构建工具代理的方法，除此以外，langChain还封装了非常多种不同的Agent实现形式，大家可以在这个链接中查看到所有LangChain中已经集成的Agent实现形式：\n",
    "推荐的Agent创建函数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e8640b2-0d11-4372-87a6-2720462b5c04",
   "metadata": {},
   "source": [
    "|函数名|功能描述|适用场景|\n",
    "|---|---|---|\n",
    "|create_tool_calling_agent|创建使用工具的Agent|通用工具调用|\n",
    "|create_openai_tools_agent|创建OpenAI工具Agent|OpenAI模型专用|\n",
    "|create_openai_functions_agent|创建OpenAI函数Agent|OpenAI函数调用|\n",
    "|create_react_agent|创建ReAct推理Agent|推理+行动模式|\n",
    "|create_structured_chat_agent|创建结构化聊天Agent|多输入工具支持|\n",
    "|create_conversational_retrieval_agent|创建对话检索Agent|检索增强对话|\n",
    "|create_json_chat_agent|创建JSON聊天Agent|JSON格式交互|\n",
    "|create_xml_agent|创建XML格式Agent|XML逻辑格式|\n",
    "|create_self_ask_with_search_agent|创建自问自答搜索Agent|自主搜索推理|"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5ca0c036-9797-4bed-8ad5-305683662231",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[2mResolved \u001b[1m176 packages\u001b[0m \u001b[2min 1ms\u001b[0m\u001b[0m\n",
      "\u001b[2mAudited \u001b[1m170 packages\u001b[0m \u001b[2min 0.06ms\u001b[0m\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "! uv add playwright lxml langchain_community beautifulsoup4 reportlab "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "1e41f666-6f0d-43b0-9b79-ea81963ac1ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "! playwright install"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5891e057-7993-4173-895b-397ad7265d6d",
   "metadata": {},
   "source": [
    "首先是需要用代理工具初始化同步 Playwright 浏览器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "4116afd7-65fb-4109-a5a5-8285b1460b9d",
   "metadata": {},
   "outputs": [
    {
     "ename": "Error",
     "evalue": "It looks like you are using Playwright Sync API inside the asyncio loop.\nPlease use the Async API instead.",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mError\u001b[39m                                     Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[15]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlangchain_community\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mtools\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mplaywright\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mutils\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m create_sync_playwright_browser\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m sync_browser = \u001b[43mcreate_sync_playwright_browser\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m      3\u001b[39m toolkit = PlayWrightBrowserToolkit.from_browser(sync_browser=sync_browser)\n\u001b[32m      4\u001b[39m tools = toolkit.get_tools()\n",
      "\u001b[36mFile \u001b[39m\u001b[32mD:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\langchain_community\\tools\\playwright\\utils.py:88\u001b[39m, in \u001b[36mcreate_sync_playwright_browser\u001b[39m\u001b[34m(headless, args)\u001b[39m\n\u001b[32m     76\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m     77\u001b[39m \u001b[33;03mCreate a playwright browser.\u001b[39;00m\n\u001b[32m     78\u001b[39m \n\u001b[32m   (...)\u001b[39m\u001b[32m     84\u001b[39m \u001b[33;03m    SyncBrowser: The playwright browser.\u001b[39;00m\n\u001b[32m     85\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m     86\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mplaywright\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01msync_api\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m sync_playwright\n\u001b[32m---> \u001b[39m\u001b[32m88\u001b[39m browser = \u001b[43msync_playwright\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstart\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     89\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m browser.chromium.launch(headless=headless, args=args)\n",
      "\u001b[36mFile \u001b[39m\u001b[32mD:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\playwright\\sync_api\\_context_manager.py:84\u001b[39m, in \u001b[36mPlaywrightContextManager.start\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m     83\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mstart\u001b[39m(\u001b[38;5;28mself\u001b[39m) -> SyncPlaywright:\n\u001b[32m---> \u001b[39m\u001b[32m84\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[34;43m__enter__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32mD:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\playwright\\sync_api\\_context_manager.py:47\u001b[39m, in \u001b[36mPlaywrightContextManager.__enter__\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m     45\u001b[39m             \u001b[38;5;28mself\u001b[39m._own_loop = \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[32m     46\u001b[39m         \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._loop.is_running():\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m             \u001b[38;5;28;01mraise\u001b[39;00m Error(\n\u001b[32m     48\u001b[39m \u001b[38;5;250m                \u001b[39m\u001b[33;03m\"\"\"It looks like you are using Playwright Sync API inside the asyncio loop.\u001b[39;00m\n\u001b[32m     49\u001b[39m \u001b[33;03mPlease use the Async API instead.\"\"\"\u001b[39;00m\n\u001b[32m     50\u001b[39m             )\n\u001b[32m     52\u001b[39m         \u001b[38;5;66;03m# Create a new fiber for the protocol dispatcher. It will be pumping events\u001b[39;00m\n\u001b[32m     53\u001b[39m         \u001b[38;5;66;03m# until the end of times. We will pass control to that fiber every time we\u001b[39;00m\n\u001b[32m     54\u001b[39m         \u001b[38;5;66;03m# block while waiting for a response.\u001b[39;00m\n\u001b[32m     55\u001b[39m         \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgreenlet_main\u001b[39m() -> \u001b[38;5;28;01mNone\u001b[39;00m:\n",
      "\u001b[31mError\u001b[39m: It looks like you are using Playwright Sync API inside the asyncio loop.\nPlease use the Async API instead."
     ]
    }
   ],
   "source": [
    "    from langchain_community.tools.playwright.utils import create_sync_playwright_browser\n",
    "    sync_browser = create_sync_playwright_browser()\n",
    "    toolkit = PlayWrightBrowserToolkit.from_browser(sync_browser=sync_browser)\n",
    "    tools = toolkit.get_tools()\n",
    "    # 通过 LangChain 创建 OpenAI 工具代理\n",
    "    agent = create_openai_tools_agent(model, tools, prompt)\n",
    "\n",
    "    # 通过 AgentExecutor 执行代理\n",
    "    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed167954-f0f8-4d85-ace5-fc7bc3268ba2",
   "metadata": {},
   "source": [
    "完整的代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "d9930447-5e4c-4cae-a478-0195c47d1e87",
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "This event loop is already running",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mRuntimeError\u001b[39m                              Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 41\u001b[39m\n\u001b[32m     38\u001b[39m     \u001b[38;5;28mprint\u001b[39m(response)\n\u001b[32m     40\u001b[39m \u001b[38;5;66;03m# 在 Jupyter Notebook 中运行异步主函数\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m41\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m main()\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 21\u001b[39m, in \u001b[36mmain\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m     19\u001b[39m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mmain\u001b[39m():\n\u001b[32m     20\u001b[39m     \u001b[38;5;66;03m# 异步初始化 Playwright 浏览器\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m21\u001b[39m     async_browser = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[43mcreate_async_playwright_browser\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     22\u001b[39m     toolkit = PlayWrightBrowserToolkit.from_browser(async_browser=async_browser)\n\u001b[32m     23\u001b[39m     tools = toolkit.get_tools()\n",
      "\u001b[36mFile \u001b[39m\u001b[32mD:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\langchain_community\\tools\\playwright\\utils.py:69\u001b[39m, in \u001b[36mcreate_async_playwright_browser\u001b[39m\u001b[34m(headless, args)\u001b[39m\n\u001b[32m     57\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m     58\u001b[39m \u001b[33;03mCreate an async playwright browser.\u001b[39;00m\n\u001b[32m     59\u001b[39m \n\u001b[32m   (...)\u001b[39m\u001b[32m     65\u001b[39m \u001b[33;03m    AsyncBrowser: The playwright browser.\u001b[39;00m\n\u001b[32m     66\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m     67\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mplaywright\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01masync_api\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m async_playwright\n\u001b[32m---> \u001b[39m\u001b[32m69\u001b[39m browser = \u001b[43mrun_async\u001b[49m\u001b[43m(\u001b[49m\u001b[43masync_playwright\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstart\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m run_async(browser.chromium.launch(headless=headless, args=args))\n",
      "\u001b[36mFile \u001b[39m\u001b[32mD:\\LangChain\\Jupyter\\.venv\\Lib\\site-packages\\langchain_community\\tools\\playwright\\utils.py:105\u001b[39m, in \u001b[36mrun_async\u001b[39m\u001b[34m(coro)\u001b[39m\n\u001b[32m     96\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Run an async coroutine.\u001b[39;00m\n\u001b[32m     97\u001b[39m \n\u001b[32m     98\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m   (...)\u001b[39m\u001b[32m    102\u001b[39m \u001b[33;03m    T: The result of the coroutine.\u001b[39;00m\n\u001b[32m    103\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m    104\u001b[39m event_loop = asyncio.get_event_loop()\n\u001b[32m--> \u001b[39m\u001b[32m105\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mevent_loop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun_until_complete\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcoro\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\asyncio\\base_events.py:667\u001b[39m, in \u001b[36mBaseEventLoop.run_until_complete\u001b[39m\u001b[34m(self, future)\u001b[39m\n\u001b[32m    656\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Run until the Future is done.\u001b[39;00m\n\u001b[32m    657\u001b[39m \n\u001b[32m    658\u001b[39m \u001b[33;03mIf the argument is a coroutine, it is wrapped in a Task.\u001b[39;00m\n\u001b[32m   (...)\u001b[39m\u001b[32m    664\u001b[39m \u001b[33;03mReturn the Future's result, or raise its exception.\u001b[39;00m\n\u001b[32m    665\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m    666\u001b[39m \u001b[38;5;28mself\u001b[39m._check_closed()\n\u001b[32m--> \u001b[39m\u001b[32m667\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_check_running\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    669\u001b[39m new_task = \u001b[38;5;129;01mnot\u001b[39;00m futures.isfuture(future)\n\u001b[32m    670\u001b[39m future = tasks.ensure_future(future, loop=\u001b[38;5;28mself\u001b[39m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\asyncio\\base_events.py:626\u001b[39m, in \u001b[36mBaseEventLoop._check_running\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    624\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_check_running\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[32m    625\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.is_running():\n\u001b[32m--> \u001b[39m\u001b[32m626\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mThis event loop is already running\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m    627\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m events._get_running_loop() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m    628\u001b[39m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[32m    629\u001b[39m             \u001b[33m'\u001b[39m\u001b[33mCannot run the event loop while another loop is running\u001b[39m\u001b[33m'\u001b[39m)\n",
      "\u001b[31mRuntimeError\u001b[39m: This event loop is already running"
     ]
    }
   ],
   "source": [
    "from langchain_community.agent_toolkits import PlayWrightBrowserToolkit\n",
    "from langchain_community.tools.playwright.utils import create_async_playwright_browser\n",
    "\n",
    "from langchain import hub\n",
    "from langchain.agents import AgentExecutor, create_openai_tools_agent\n",
    "from langchain.chat_models import init_chat_model\n",
    "import asyncio\n",
    "import os\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# 加载环境变量\n",
    "load_dotenv(override=True)\n",
    "\n",
    "# 获取DeepSeek API密钥\n",
    "DeepSeek_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\n",
    "if not DeepSeek_API_KEY:\n",
    "    raise ValueError(\"DEEPSEEK_API_KEY 环境变量未设置\")\n",
    "\n",
    "async def main():\n",
    "    # 异步初始化 Playwright 浏览器\n",
    "    async_browser = await create_async_playwright_browser()\n",
    "    toolkit = PlayWrightBrowserToolkit.from_browser(async_browser=async_browser)\n",
    "    tools = toolkit.get_tools()\n",
    "    \n",
    "    # 通过 LangChain Hub 拉取提示词模版\n",
    "    prompt = await hub.apull(\"hwchase17/openai-tools-agent\")\n",
    "    \n",
    "    # 初始化模型\n",
    "    model = await init_chat_model(\"deepseek-chat\", model_provider=\"deepseek\", api_key=DeepSeek_API_KEY)\n",
    "    \n",
    "    # 通过 LangChain 创建 OpenAI 工具代理\n",
    "    agent = create_openai_tools_agent(model, tools, prompt)\n",
    "    \n",
    "    # 通过 AgentExecutor 执行代理\n",
    "    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "    command = {\"input\": \"访问这个网站 https://github.com/fufankeji/MateGen/blob/main/README_zh.md 并帮我总结一下这个网站的内容\"}\n",
    "    response = await agent_executor.ainvoke(command)\n",
    "    print(response)\n",
    "\n",
    "# 在 Jupyter Notebook 中运行异步主函数\n",
    "await main()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
